package jee.boot.module.file.azure;

import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.CloudBlockBlob;
import jee.boot.common.basic.ServiceException;
import jee.boot.common.standard.DocumentStorageClient;
import jee.boot.common.utils.DateUtils;
import jee.boot.common.utils.FileUtils;
import jee.boot.common.utils.SpringContextHolder;
import jee.boot.entity.file.Attachment;
import jee.boot.module.file.attachment.service.AttachmentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import javax.activation.MimetypesFileTypeMap;
import java.io.*;
import java.net.URI;
import java.util.Date;
import java.util.UUID;

import static com.microsoft.azure.storage.StorageCredentials.tryParseCredentials;

/**
 * 使用微软云存储文件
 */
public class AzureFileStore implements DocumentStorageClient {
    private Logger logger = LoggerFactory.getLogger(getClass());
    private AzureFileConfig config;
    private AttachmentService attachmentService;

    public AzureFileStore(AzureFileConfig config) {
        this.config = config;
    }

    /**
     * 上传形式的文件上传
     *
     * @param file 上传形式的文件
     * @return SysAttachment对象, 包含附件id, fileName等信息
     */
    @Override
    public Attachment uploadFromMultipartFile(MultipartFile file) {
        // 存储附件数据
        Attachment attachment = new Attachment();
        attachment.setFileName(file.getOriginalFilename());//文件真实名称
        attachment.setFileType(FileUtils.getFileExtension(file.getOriginalFilename()));//文件拓展名
        attachment.setContentType(file.getContentType());//文件类型
        attachment.setFileId(UUID.randomUUID().toString().replaceAll("-", ""));//文件id(存储的文件名称)
        attachment.setSavePath(getSavePath());//存储目录
        // 存储附件文件
        logger.info("Azure存储文件:" + getFullPath(attachment));
        try {
            InputStream inputStream = file.getInputStream(); // The call to upload supports FileInputStream efficiently.
            CloudBlobContainer container = createCloudBlobContainer(attachment.getSavePath());
            CloudBlockBlob blob = container.getBlockBlobReference(getFullPath(attachment));
            blob.upload(file.getInputStream(), file.getSize());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new ServiceException(1401);
        }
        getAttachmentService().save(attachment);
        return attachment;

    }

    /**
     * 输入流上传至文件服务器
     *
     * @param inputStream 输入数据流
     * @param fileName    文件名
     * @return @return SysAttachment对象,包含附件id,fileName等信息
     */
    @Override
    public Attachment uploadFromInputStream(InputStream inputStream, String fileName) {
        // 存储附件数据
        Attachment attachment = new Attachment();
        attachment.setFileName(fileName);//文件真实名称
        attachment.setFileType(FileUtils.getFileExtension(fileName));//文件拓展名
        attachment.setContentType(null);//文件类型
        attachment.setFileId(UUID.randomUUID().toString().replaceAll("-", ""));//文件id(存储的文件名称)
        attachment.setSavePath(getSavePath());//存储目录
        // 存储附件文件
        logger.info("Azure存储文件:" + getFullPath(attachment));
        try {
            CloudBlobContainer container = createCloudBlobContainer(attachment.getSavePath());
            CloudBlockBlob blob = container.getBlockBlobReference(getFullPath(attachment));
            blob.upload(inputStream, inputStream.available());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new ServiceException(1401);
        }
        getAttachmentService().save(attachment);
        return attachment;

    }


    /**
     * 文件上传至文件服务器
     *
     * @param file 临时文件
     * @return @return SysAttachment对象,包含附件id,fileName等信息
     */
    @Override
    public Attachment uploadFromFile(File file) {
        // 存储附件数据
        Attachment attachment = new Attachment();
        attachment.setFileName(file.getName());//文件真实名称
        attachment.setFileType(FileUtils.getFileExtension(file.getName()));//文件拓展名
        attachment.setContentType(new MimetypesFileTypeMap().getContentType(file));//文件类型
        attachment.setFileId(UUID.randomUUID().toString().replaceAll("-", ""));//文件id(存储的文件名称)
        attachment.setSavePath(getSavePath());//存储目录
        // 存储附件文件
        logger.info("Azure存储文件:" + getFullPath(attachment));

        try {
            InputStream inputStream = new FileInputStream(file); // The call to upload supports FileInputStream efficiently.
            CloudBlobContainer container = createCloudBlobContainer(attachment.getSavePath());
            CloudBlockBlob blob = container.getBlockBlobReference(getFullPath(attachment));
            blob.upload(inputStream, inputStream.available());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new ServiceException(1401);
        }
        getAttachmentService().save(attachment);
        return attachment;
    }


    /**
     * 从文件服务器下载一个文件
     *
     * @param attachmentId 附件ID
     * @return 文件
     */
    @Override
    public File downloadToFile(String attachmentId) throws FileNotFoundException {
        Attachment attachment = getAttachmentService().get(new Attachment(attachmentId));
        if (attachment == null) {
            logger.error("附件不存在:" + attachmentId);
            throw new FileNotFoundException(attachmentId);
        }
        logger.info("Azure下载文件:" + getFullPath(attachment));
        CloudBlobContainer container = createCloudBlobContainer(attachment.getSavePath());
        try {
            File file = File.createTempFile(attachment.getFileId(), attachment.getFileType());// 生成临时文件
            CloudBlockBlob blob = container.getBlockBlobReference(getFullPath(attachment));
            blob.downloadToFile(file.getAbsolutePath());
            return file;
        } catch (Exception e) {
            logger.error("下载文件失败:" + e.getMessage());
            throw new ServiceException(1402);
        }
    }

    /**
     * 从文件服务器下载一个文件到输出流
     *
     * @param attachmentId 附件ID
     * @return 文件流
     */
    @Override
    public OutputStream downloadToOutputStream(String attachmentId) throws FileNotFoundException {
        return new FileOutputStream(downloadToFile(attachmentId));

    }

    /**
     * 删除文件
     *
     * @param attachmentId 附件ID
     */
    @Override
    public void deleteFile(String attachmentId) throws FileNotFoundException {
        Attachment attachment = getAttachmentService().get(new Attachment(attachmentId));
        if (attachment == null) {
            logger.error("附件不存在:" + attachmentId);
            throw new FileNotFoundException(attachmentId);
        }
        logger.info("Azure删除文件:" + getFullPath(attachment));
        CloudBlobContainer container = createCloudBlobContainer(attachment.getSavePath());
        try {
            CloudBlockBlob blob = container.getBlockBlobReference(getFullPath(attachment));
            blob.deleteIfExists();
        } catch (Exception e) {
            logger.error("下载文件失败:" + e.getMessage());
            throw new ServiceException(1402);
        }
        getAttachmentService().delete(new Attachment(attachmentId));
    }

    /**
     * 创建一个容器(目录),用于管理blob上的文件
     *
     * @param containerPath 容器,目录
     * @return
     */
    private CloudBlobContainer createCloudBlobContainer(String containerPath) {
        CloudBlobClient blobClient = null;
        CloudBlobContainer container = null;
        try {
            blobClient = new CloudBlobClient(new URI(config.getServiceUrl()), tryParseCredentials(config.getSasToken()));
            container = blobClient.getContainerReference(containerPath);
            container.createIfNotExists();
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            throw new ServiceException(1403);
        }
        if (container == null) {
            throw new ServiceException(1403);
        }
        return container;
    }

    /**
     * 附件数据存储服务
     *
     * @return
     */
    public AttachmentService getAttachmentService() {
        if (attachmentService == null) {
            attachmentService = SpringContextHolder.getBean(AttachmentService.class);
        }
        return attachmentService;
    }

    /**
     * 本地存储按/年月/ 分目录进行存储 eq: ${basePath}/202005/
     *
     * @return
     */
    public String getSavePath() {
        String path = DateUtils.formatDate(new Date(), "yyyyMM" + File.separator);
        return path;
    }

    /**
     * 获取完整路径
     *
     * @param attachment
     * @return
     */
    public String getFullPath(Attachment attachment) {
        return attachment.getFileId() + "." + attachment.getFileType();
    }
}
